package top.zxybird.yun.serialization.thrift;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import lombok.Getter;
import top.zxybird.yun.serialization.thrift.model.*;

public class ThriftBinarySerializer extends ThriftSerializer {

  // ======================================== 基本类型 ========================================

  private static Function<Byte, List<Byte>> serializeByte = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }
    return Lists.newArrayList(x);
  };

  private static Function<Boolean, List<Byte>> serializeBoolean = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }

    if (x) {
      return Lists.newArrayList((byte) 1);
    } else {
      return Lists.newArrayList((byte) 0);
    }
  };

  private static Function<Short, List<Byte>> serializeShort = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }

    return Lists.newArrayList((byte) (0xff & (x >> 8)), (byte) (0xff & (x)));
  };

  private static Function<Integer, List<Byte>> serializeInteger = (x) -> {

    if (null == x) {
      return Collections.emptyList();
    }

    List<Byte> out = Lists.newArrayListWithExpectedSize(4);

    out.add((byte) (0xff & (x >> 24)));
    out.add((byte) (0xff & (x >> 16)));
    out.add((byte) (0xff & (x >> 8)));
    out.add((byte) (0xff & (x)));

    return out;

  };

  private static Function<Long, List<Byte>> serializeLong = (x) -> {

    if (null == x) {
      return Collections.emptyList();
    }

    List<Byte> list = Lists.newArrayListWithExpectedSize(8);

    list.add((byte) (0xff & (x >> 56)));
    list.add((byte) (0xff & (x >> 48)));
    list.add((byte) (0xff & (x >> 32)));
    list.add((byte) (0xff & (x >> 24)));
    list.add((byte) (0xff & (x >> 16)));
    list.add((byte) (0xff & (x >> 8)));
    list.add((byte) (0xff & (x)));

    return list;

  };

  private static Function<Double, List<Byte>> serializeDouble = (x) -> {

    if (null == x) {
      return Collections.emptyList();
    }

    long double_value = Double.doubleToLongBits(x);

    List<Byte> list = Lists.newArrayListWithExpectedSize(8);

    list.add((byte) (0xff & (double_value >> 56)));
    list.add((byte) (0xff & (double_value >> 48)));
    list.add((byte) (0xff & (double_value >> 32)));
    list.add((byte) (0xff & (double_value >> 24)));
    list.add((byte) (0xff & (double_value >> 16)));
    list.add((byte) (0xff & (double_value >> 8)));
    list.add((byte) (0xff & (double_value)));

    return list;

  };

  private static Function<String, List<Byte>> serializeString = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }

    List<Byte> list = Lists.newArrayList();

    Integer len = x.length();

    list.addAll(Optional.fromNullable(findByTypeWithDefault(len.getClass()).function.apply(x.length()))
        .or(Collections.emptyList()));

    for (byte _byte : x.getBytes()) {
      list.add(_byte);
    }

    return list;
  };

  // ========================================= TType ========================================

  private static void abstractFunction(List<Byte> out, TType in) {
    // 放置类型
    out.addAll(Optional.fromNullable(serializeByte.apply(in.getType())).or(Collections.emptyList()));
    // 放置编号
    out.addAll(Optional.fromNullable(serializeShort.apply(in.getNumber())).or(Collections.emptyList()));

    // 放置值
    out.addAll(Optional.fromNullable(findByTypeWithDefault(in.getValue().getClass()).function.apply(in.getValue()))
        .or(Collections.emptyList()));
  }

  /**
   * Stop类型需要1个字节
   * <p>
   * 返回(byte)0
   */
  private static Function<TStop, List<Byte>> serializeTStop = (x) -> Lists.newArrayList(TStop.getInstance().getValue());

  /**
   * Void类型需要1个字节
   * <p>
   * 返回空
   */
  private static Function<TVoid, List<Byte>> serializeTVoid = (x) -> Collections.emptyList();

  /**
   * boolean类型需要4个字节
   * <p>
   * 1个字节的类型 2个字节的编号 1个字节的值
   */
  private static Function<TBoolean, List<Byte>> serializeTBoolean = (x) -> {

    if (null == x) {
      return Collections.emptyList();
    }

    Preconditions.checkArgument(null != x.getValue());

    List<Byte> out = Lists.newArrayListWithExpectedSize(4);

    abstractFunction(out, x);

    // 放置类型
    // out.addAll(Optional.fromNullable(serializeByte.apply(in.getType())).or(Collections.emptyList()));
    // 放置编号
    // out.addAll(Optional.fromNullable(serializeShort.apply(in.getNumber())).or(Collections.emptyList()));

    // 放置值
    // out.addAll(Optional.fromNullable(findByTypeWithDefault(in.getClass()).function.apply(in.getValue()))
    // .or(Collections.emptyList()));

    return out;

  };

  /**
   * byte类型需要4个字节
   * <p>
   * 1个字节的类型 2个字节的编号 1个字节的值
   */
  private static Function<TByte, List<Byte>> serializeTByte = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }

    Preconditions.checkArgument(null != x.getValue());

    List<Byte> out = Lists.newArrayListWithExpectedSize(4);
    abstractFunction(out, x);

    // 放置类型
    // out.addAll(Optional.fromNullable(serializeByte.apply(in.getType())).or(Collections.emptyList()));
    // 放置编号
    // out.addAll(Optional.fromNullable(serializeShort.apply(in.getNumber())).or(Collections.emptyList()));

    // 放置值
    // out.addAll(Optional.fromNullable(findByTypeWithDefault(in.getClass()).function.apply(in.getValue()))
    // .or(Collections.emptyList()));

    return out;
  };

  /**
   * double类型需要11个字节
   * <p>
   * 1个字节的类型 2个字节的编号 8个字节的值
   */
  private static Function<TDouble, List<Byte>> serializeTDouble = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }

    Preconditions.checkArgument(null != x.getValue());

    List<Byte> out = Lists.newArrayListWithExpectedSize(7);
    abstractFunction(out, x);

    // 放置类型
    // out.addAll(Optional.fromNullable(serializeByte.apply(in.getType())).or(Collections.emptyList()));
    // 放置编号
    // out.addAll(Optional.fromNullable(serializeShort.apply(in.getNumber())).or(Collections.emptyList()));

    // 放置值
    // out.addAll(Optional.fromNullable(findByTypeWithDefault(in.getClass()).function.apply(in.getValue()))
    // .or(Collections.emptyList()));

    return out;
  };

  /**
   * short类型需要5个字节
   * <p>
   * 1个字节的类型 2个字节的编号 2个字节的值
   */
  private static Function<TShort, List<Byte>> serializeTShort = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }

    Preconditions.checkArgument(null != x.getValue());

    List<Byte> out = Lists.newArrayListWithExpectedSize(5);
    abstractFunction(out, x);

    // 放置类型
    // out.addAll(Optional.fromNullable(serializeByte.apply(in.getType())).or(Collections.emptyList()));
    // 放置编号
    // out.addAll(Optional.fromNullable(serializeShort.apply(in.getNumber())).or(Collections.emptyList()));

    // 放置值
    // out.addAll(Optional.fromNullable(findByTypeWithDefault(in.getClass()).function.apply(in.getValue()))
    // .or(Collections.emptyList()));

    return out;
  };

  /**
   * int类型需要7个字节
   * <p>
   * 1个字节的类型 2个字节的编号 4个字节的值
   */
  private static Function<TInt, List<Byte>> serializeTInt = (x) -> {

    if (null == x) {
      return Collections.emptyList();
    }

    Preconditions.checkArgument(null != x.getValue());

    List<Byte> out = Lists.newArrayListWithExpectedSize(5);
    abstractFunction(out, x);

    // 放置类型
    // out.addAll(Optional.fromNullable(serializeByte.apply(in.getType())).or(Collections.emptyList()));
    // 放置编号
    // out.addAll(Optional.fromNullable(serializeShort.apply(in.getNumber())).or(Collections.emptyList()));

    // 放置值
    // out.addAll(Optional.fromNullable(findByTypeWithDefault(in.getClass()).function.apply(in.getValue()))
    // .or(Collections.emptyList()));

    return out;

  };

  /**
   * long类型需要11个字节
   * <p>
   * 1个字节的类型 2个字节的编号 8个字节的值
   */
  private static Function<TLong, List<Byte>> serializeTLong = (x) -> {

    if (null == x) {
      return Collections.emptyList();
    }

    Preconditions.checkArgument(null != x.getValue());

    List<Byte> out = Lists.newArrayListWithExpectedSize(5);
    abstractFunction(out, x);

    // 放置类型
    // out.addAll(Optional.fromNullable(serializeByte.apply(in.getType())).or(Collections.emptyList()));
    // 放置编号
    // out.addAll(Optional.fromNullable(serializeShort.apply(in.getNumber())).or(Collections.emptyList()));

    // 放置值
    // out.addAll(Optional.fromNullable(findByTypeWithDefault(in.getClass()).function.apply(in.getValue()))
    // .or(Collections.emptyList()));

    return out;

  };

  /**
   * long类型需要11个字节
   * <p>
   * 1个字节的类型 2个字节的编号 8个字节的值
   */
  private static Function<TString, List<Byte>> serializeTString = (x) -> {

    if (null == x) {
      return Collections.emptyList();
    }

    Preconditions.checkArgument(null != x.getValue());

    List<Byte> out = Lists.newArrayListWithExpectedSize(7);
    abstractFunction(out, x);

    // 放置类型
    // out.addAll(Optional.fromNullable(serializeByte.apply(in.getType())).or(Collections.emptyList()));
    // 放置编号
    // out.addAll(Optional.fromNullable(serializeShort.apply(in.getNumber())).or(Collections.emptyList()));

    // 放置值
    // out.addAll(Optional.fromNullable(findByTypeWithDefault(in.getClass()).function.apply(in.getValue()))
    // .or(Collections.emptyList()));
    // ByteListHelper.serialize(out, in.getArgs());

    return out;

  };

  private static Function<TStruct, List<Byte>> serializeTStruct = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }

    Preconditions.checkArgument(null != x.getAllField());

    List<Byte> out = Lists.newArrayList();
    // 放置类型
    // TypeToken.of(in.getType());

    out.addAll(Optional.fromNullable(findByTypeWithDefault(x.getType().getClass()).function.apply(x.getType()))
        .or(Collections.emptyList()));
    // 放置编号
    out.addAll(Optional.fromNullable(findByTypeWithDefault(x.getNumber().getClass()).function.apply(x.getNumber()))
        .or(Collections.emptyList()));
    // 放置值

    List<TType> fieldList = IntStream.range(1, x.getAllField().size() + 1).boxed().map(index -> {
      TType field = x.getAllField().get(index - 1);
      field.setNumber(index.shortValue());
      return field;
    }).collect(Collectors.toList());

    for (TType field : fieldList) {
      out.addAll(Optional.fromNullable(findByTypeWithDefault(field.getClass()).function.apply(field))
          .or(Collections.emptyList()));
    }

    out.add(TStop.getInstance().getValue());

    return out;
  };

  private static Function<TMessage, List<Byte>> serializeTMessage = (x) -> {
    if (null == x) {
      return Collections.emptyList();
    }
    Preconditions.checkArgument(null != x.getName());

    List<Byte> out = Lists.newArrayList();

    // 类型放置在version后最后8位
    Integer version = ThriftConstant.VERSION_1 | x.getType();
    out.addAll(Optional.fromNullable(findByTypeWithDefault(version.getClass()).function.apply(version))
        .or(Collections.emptyList()));
    out.addAll(Optional.fromNullable(findByTypeWithDefault(x.getName().getClass()).function.apply(x.getName()))
        .or(Collections.emptyList()));
    out.addAll(Optional.fromNullable(findByTypeWithDefault(x.getSeqid().getClass()).function.apply(x.getSeqid()))
        .or(Collections.emptyList()));

    List<TType> fieldList = IntStream.range(1, x.getAllField().size() + 1).boxed().map(index -> {
      TType field = x.getAllField().get(index - 1);
      field.setNumber(index.shortValue());
      return field;
    }).collect(Collectors.toList());

    for (TType field : fieldList) {
      out.addAll(Optional.fromNullable(findByTypeWithDefault(field.getClass()).function.apply(field))
          .or(Collections.emptyList()));
    }

    out.add(TStop.getInstance().getValue());

    return out;
  };

  public enum Type {

    /**
     * byte
     */
    _byte(byte.class, serializeByte),
    /**
     * Byte
     */
    _Byte(Byte.class, serializeByte),
    /**
     * boolean
     */
    _boolean(boolean.class, serializeBoolean),
    /**
     * Boolean
     */
    _Boolean(Boolean.class, serializeBoolean),
    /**
     * short
     */
    _short(short.class, serializeShort),
    /**
     * Short
     */
    _Short(Short.class, serializeShort),
    /**
     * int
     */
    _int(int.class, serializeInteger),
    /**
     * Integer
     */
    _Integer(Integer.class, serializeInteger),
    /**
     * long
     */
    _long(long.class, serializeLong),
    /**
     * Long
     */
    _Long(Long.class, serializeLong),
    /**
     * double
     */
    _double(double.class, serializeDouble),
    /**
     * Double
     */
    _Doulbe(Double.class, serializeDouble),
    /**
     * String
     */
    _String(String.class, serializeString),
    /**
     * TStop
     */
    _TStop(TStop.class, serializeTStop),
    /**
     * TVoid
     */
    _TVoid(TVoid.class, serializeTVoid),
    /**
     * TBoolean
     */
    _TBoolean(TBoolean.class, serializeTBoolean),
    /**
     * TByte
     */
    _TByte(TByte.class, serializeTByte),
    /**
     * TDouble
     */
    _TDouble(TDouble.class, serializeTDouble),
    /**
     * TShort
     */
    _TShort(TShort.class, serializeTShort),
    /**
     * TInt
     */
    _TInt(TInt.class, serializeTInt),
    /**
     * TLong
     */
    _TLong(TLong.class, serializeTLong),
    /**
     * TString
     */
    _TString(TString.class, serializeTString),
    /**
     * TStruct
     */
    _TStruct(TStruct.class, serializeTStruct),
    /**
     * TMessage
     */
    _TMessage(TMessage.class, serializeTMessage);

    private Class<?> clazz;

    @Getter
    private Function<Object, List<Byte>> function;
    // private Function<?, List<Byte>> function;
    // private Function<? extends Object, List<Byte>> function;

    @SuppressWarnings("unchecked")
    Type(Class<?> clazz, Function function) {
      this.clazz = clazz;
      this.function = function;
    }
  }

  private static Map<Class, Type> typeMap = Arrays.stream(Type.values())
      .collect(Collectors.toMap(x -> x.clazz, x -> x, (o1, o2) -> o1));

  public static Type findByTypeWithDefault(Class clazz) {

    if (null == clazz) {
      return null;
    }
    Type type = typeMap.get(clazz);

    if (null != type) {
      return type;
    } else {
      return Type._TStruct;
    }

  }

}
